package com.bigshen.spring.framework.aop.support;

import com.bigshen.spring.framework.aop.aspect.AopAdvice;
import com.bigshen.spring.framework.aop.config.AopConfig;
import lombok.Data;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author eddy.shen
 * @Date 2020/4/19 15:56
 **/
public class AopAdvisedSupport {

    private Object target;

    private Class targetClass;

    private AopConfig aopConfig;

    private Pattern pointCutClassPattern;

    private Map<Method, Map<String, AopAdvice>> methodCache;

    public AopAdvisedSupport(AopConfig aopConfig) {
        this.aopConfig = aopConfig;
    }


    /**
     * 解析配置文件的方法
     * 构造正则类匹配器Pattern,针对Spring Bean class进行匹配，看是否需要进行AOP
     * 解析提前预备好 Spring Bean Class内的每个method所需的通知，记录其映射关系
     */
    private void parse() {

        //把Spring的Excpress变成Java能够识别的正则表达式
        String pointCut = aopConfig.getPointCut()
                .replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");


        //保存专门匹配Class的正则
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));

        //享元的共享池
        methodCache = new HashMap<Method, Map<String, AopAdvice>>();
        //保存专门匹配方法的正则
        Pattern pointCutPattern = Pattern.compile(pointCut);
        try{
            Class aspectClass = Class.forName(this.aopConfig.getAspectClass());
            Map<String,Method> aspectMethods = new HashMap<String, Method>();
            for (Method method : aspectClass.getMethods()) {
                aspectMethods.put(method.getName(),method);
            }

            for (Method method : this.targetClass.getMethods()) {
                String methodString = method.toString();
                if(methodString.contains("throws")){
                    methodString = methodString.substring(0,methodString.lastIndexOf("throws")).trim();
                }

                Matcher matcher = pointCutPattern.matcher(methodString);
                if(matcher.matches()){
                    Map<String,AopAdvice> advices = new HashMap<String, AopAdvice>();

                    if(!(null == aopConfig.getAspectBefore() || "".equals(aopConfig.getAspectBefore()))){
                        Method beforeMethod = aspectMethods.get(aopConfig.getAspectBefore());
                        advices.put("before", new AopAdvice(beforeMethod, aspectClass.newInstance()));
                    }
                    if(!(null == aopConfig.getAspectAfter() || "".equals(aopConfig.getAspectAfter()))){
                        Method afterMethod = aspectMethods.get(aopConfig.getAspectAfter());
                        advices.put("after", new AopAdvice(afterMethod, aspectClass.newInstance()));
                    }
                    if(!(null == aopConfig.getAspectAfterThrow() || "".equals(aopConfig.getAspectAfterThrow()))){
                        Method afterThrowingMethod = aspectMethods.get(aopConfig.getAspectAfterThrow());
                        AopAdvice advice = new AopAdvice(afterThrowingMethod, aspectClass.newInstance());
                        advice.setThrowName(aopConfig.getAspectAfterThrowName());
                        advices.put("afterThrow", advice);
                    }

                    //跟目标代理类的业务方法和Advices建立一对多个关联关系，以便在Porxy类中获得
                    methodCache.put(method,advices);
                }
            }

        }catch(Exception e){
            e.printStackTrace();
        }

    }


    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public Class getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    public AopConfig getAopConfig() {
        return aopConfig;
    }

    public void setAopConfig(AopConfig aopConfig) {
        this.aopConfig = aopConfig;
    }

    public Pattern getPointCutClassPattern() {
        return pointCutClassPattern;
    }

    public void setPointCutClassPattern(Pattern pointCutClassPattern) {
        this.pointCutClassPattern = pointCutClassPattern;
    }

    public Map<Method, Map<String, AopAdvice>> getMethodCache() {
        return methodCache;
    }

    public void setMethodCache(Map<Method, Map<String, AopAdvice>> methodCache) {
        this.methodCache = methodCache;
    }

    public boolean pointCutMatchClass() {

        return this.getPointCutClassPattern().matcher(this.targetClass.toString()).matches();
    }
}
